/**
 * Copyright ${license.git.copyrightYears} the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ibatis.binding;

import org.apache.ibatis.annotations.*;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.domain.blog.Author;
import org.apache.ibatis.domain.blog.Blog;
import org.apache.ibatis.domain.blog.DraftPost;
import org.apache.ibatis.domain.blog.Post;
import org.apache.ibatis.mapping.FetchType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.List;
import java.util.Map;

@CacheNamespace(readWrite = false)
public interface BoundBlogMapper {

    //======================================================

    Blog selectBlogWithPostsUsingSubSelect(int id);

    //======================================================

    int selectRandom();

    //======================================================

    @Select({"SELECT * FROM blog"})
    @MapKey("id")
    Map<Integer, Blog> selectBlogsAsMapById();

    @Select({"SELECT * FROM blog ORDER BY id"})
    @MapKey("id")
    Map<Integer, Blog> selectRangeBlogsAsMapById(RowBounds rowBounds);

    //======================================================

    @Select({
            "SELECT *",
            "FROM blog"
    })
    List<Blog> selectBlogs();

    @Select({
            "SELECT *",
            "FROM blog",
            "ORDER BY id"
    })
    @ResultType(Blog.class)
    void collectRangeBlogs(ResultHandler<Object> blog, RowBounds rowBounds);


    @Select({
            "SELECT *",
            "FROM blog",
            "ORDER BY id"
    })
    Cursor<Blog> openRangeBlogs(RowBounds rowBounds);

    //======================================================

    List<Blog> selectBlogsFromXML();

    //======================================================

    @Select({
            "SELECT *",
            "FROM blog"
    })
    List<Map<String, Object>> selectBlogsAsMaps();

    //======================================================

    @SelectProvider(type = BoundBlogSql.class, method = "selectBlogsSql")
    List<Blog> selectBlogsUsingProvider();

    //======================================================

    @Select("SELECT * FROM post ORDER BY id")
    @TypeDiscriminator(
            column = "draft",
            javaType = String.class,
            cases = {@Case(value = "1", type = DraftPost.class)}
    )
    List<Post> selectPosts();

    //======================================================

    @Select("SELECT * FROM post ORDER BY id")
    @Results({
            @Result(id = true, property = "id", column = "id")
    })
    @TypeDiscriminator(
            column = "draft",
            javaType = int.class,
            cases = {@Case(value = "1", type = DraftPost.class,
                    results = {@Result(id = true, property = "id", column = "id")})}
    )
    List<Post> selectPostsWithResultMap();

    //======================================================

    @Select("SELECT * FROM " +
            "blog WHERE id = #{id}")
    Blog selectBlog(int id);

    //======================================================

    @Select("SELECT * FROM " +
            "blog WHERE id = #{id}")
    @ConstructorArgs({
            @Arg(column = "id", javaType = int.class, id = true),
            @Arg(column = "title", javaType = String.class),
            @Arg(column = "author_id", javaType = Author.class, select = "org.apache.ibatis.binding.BoundAuthorMapper.selectAuthor"),
            @Arg(column = "id", javaType = List.class, select = "selectPostsForBlog")
    })
    Blog selectBlogUsingConstructor(int id);

    Blog selectBlogUsingConstructorWithResultMap(int i);

    Blog selectBlogUsingConstructorWithResultMapAndProperties(int i);

    Blog selectBlogUsingConstructorWithResultMapCollection(int i);

    Blog selectBlogByIdUsingConstructor(int id);

    //======================================================

    @Select("SELECT * FROM " +
            "blog WHERE id = #{id}")
    Map<String, Object> selectBlogAsMap(Map<String, Object> params);

    //======================================================

    @Select("SELECT * FROM " +
            "post WHERE subject like #{query}")
    List<Post> selectPostsLike(RowBounds bounds, String query);

    //======================================================

    @Select("SELECT * FROM " +
            "post WHERE subject like #{subjectQuery} and body like #{bodyQuery}")
    List<Post> selectPostsLikeSubjectAndBody(RowBounds bounds,
                                             @Param("subjectQuery") String subjectQuery,
                                             @Param("bodyQuery") String bodyQuery);

    //======================================================

    @Select("SELECT * FROM " +
            "post WHERE id = #{id}")
    List<Post> selectPostsById(int id);

    //======================================================

    @Select("SELECT * FROM blog " +
            "WHERE id = #{id} AND title = #{nonExistentParam,jdbcType=VARCHAR}")
    Blog selectBlogByNonExistentParam(@Param("id") int id);

    @Select("SELECT * FROM blog " +
            "WHERE id = #{id} AND title = #{params.nonExistentParam,jdbcType=VARCHAR}")
    Blog selectBlogByNonExistentNestedParam(@Param("id") int id, @Param("params") Map<String, Object> params);

    @Select("SELECT * FROM blog WHERE id = #{id}")
    Blog selectBlogByNullParam(Integer id);

    //======================================================

    @Select("SELECT * FROM blog " +
            "WHERE id = #{0} AND title = #{1}")
    Blog selectBlogByDefault30ParamNames(int id, String title);

    @Select("SELECT * FROM blog " +
            "WHERE id = #{param1} AND title = #{param2}")
    Blog selectBlogByDefault31ParamNames(int id, String title);

    //======================================================

    @Select("SELECT * FROM blog " +
            "WHERE ${column} = #{id} AND title = #{value}")
    Blog selectBlogWithAParamNamedValue(@Param("column") String column, @Param("id") int id, @Param("value") String title);

    //======================================================

    @Select({
            "SELECT *",
            "FROM blog"
    })
    @Results({
            @Result(property = "author", column = "author_id", one = @One(select = "org.apache.ibatis.binding.BoundAuthorMapper.selectAuthor")),
            @Result(property = "posts", column = "id", many = @Many(select = "selectPostsById"))
    })
    List<Blog> selectBlogsWithAutorAndPosts();

    @Select({
            "SELECT *",
            "FROM blog"
    })
    @Results({
            @Result(property = "author", column = "author_id", one = @One(select = "org.apache.ibatis.binding.BoundAuthorMapper.selectAuthor", fetchType = FetchType.EAGER)),
            @Result(property = "posts", column = "id", many = @Many(select = "selectPostsById", fetchType = FetchType.EAGER))
    })
    List<Blog> selectBlogsWithAutorAndPostsEagerly();

}
